import cloneDeep from 'lodash/cloneDeep';
import isEqual from 'lodash/isEqual';
import pick from 'lodash/pick';
import omit from 'lodash/omit';
import has = Reflect.has;

export type KeyOfDistributive<T> = T extends unknown ? keyof T : never;

export const ObjectUtil = {
  /**
   * @author 戴俊明 <idaijunming@163.com>
   * @description 0 和 '' 为 true, null 和 undefined 为 false
   * @date 2020/6/6 0:17
   **/
  notNull: (o: unknown): boolean => {
    // eslint-disable-next-line eqeqeq
    return o != null;
  },

  keys: <T>(o: T): (KeyOfDistributive<T> & string)[] => {
    if (!o) {
      return [];
    }
    type TKeys = KeyOfDistributive<T> & string;
    if (Object.keys) {
      return Object.keys(o) as TKeys[];
    }
    const keyList: TKeys[] = [];
    for (const key in o) {
      if (has(o as never, key)) {
        keyList.push(key as never);
      }
    }
    return keyList;
  },

  has: (o: unknown, key: PropertyKey): boolean => {
    return Object.prototype.hasOwnProperty.call(o, key);
  },

  equals: <T, K extends keyof T>(a: T, b: T, ignoreKeys: K[] = []): boolean => {
    return isEqual(omit(a as never, ignoreKeys), omit(b as never, ignoreKeys));
  },

  clone: <T>(o: T): T => cloneDeep(o),

  pick: <T, K extends keyof T>(o: T, keys: K[] = []): Pick<T, K> => pick(o as unknown, keys) as Pick<T, K>,

  omit: <T, K extends keyof T>(o: T, ignoreKeys: K[] = []): Omit<T, K> => omit(o as never, ignoreKeys) as Omit<T, K>,

  assign: <T, K extends keyof T>(oldVal: T, newVal: T, ignoreKeys: K[] = []): Omit<T, K> => {
    let clone = cloneDeep(oldVal);
    clone = Object.assign(clone, newVal);
    for (const ignoreKey of ignoreKeys) {
      delete clone[ignoreKey];
    }
    return clone;
  },

  toJSON: (o: unknown): string => JSON.stringify(o),

  fromJSON: (json: string): unknown => {
    try {
      return JSON.parse(json);
    } catch (e) {
      return null;
    }
  },
};
